﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.IO;
using System.Xml.Linq;

using SpotiKat.Spotify.Core;
using SpotiKat.Spotify.Entity;
using System.Globalization;
using System.Xml;

namespace SpotiKat.Spotify.Manager
{
    internal class SpotifyManager
    {
        public static SpotifyManager Instance { get { return new SpotifyManager(); } }

        public XDocument GetResponse(string url)
        {
            try
            {
                HttpWebRequest httpRequest = (HttpWebRequest)WebRequest.Create(url);

                httpRequest.Method = "get";
                httpRequest.KeepAlive = false;
                httpRequest.ContentType = "text/xml";

                using (HttpWebResponse httpResponse = (HttpWebResponse)httpRequest.GetResponse())
                {
                    Encoding enc = System.Text.Encoding.UTF8;
                    using (StreamReader streamReader = new StreamReader(httpResponse.GetResponseStream(), enc))
                    {
                        string response = streamReader.ReadToEnd();
                        
                        XDocument xdoc = XDocument.Parse(response);

                        return xdoc;
                    }
                }
            }
            catch (Exception ex)
            {
                LogManager.Instance.LogError("Error", ex);
                throw new SpotifyCommunicationException(ex);
            }
        }

        public IList<Artist> GetArtists(XElement xelement)
        {
            IList<Artist> artists = new List<Artist>();

            foreach (XElement xel in xelement.Elements())
            {
                if (xel.Name.LocalName == "artist")
                {
                    artists.Add(GetArtist(xel));
                }
            }

            return artists;
        }

        public ArtistWithAlbums GetArtistWithAlbums(XElement xelement)
        {
            string name = null;
            double? popularity = null;
            string href = null;
            IList<Album> albums = null;

            foreach (XAttribute xattr in xelement.Attributes())
            {
                if (xattr.Name.LocalName == "href")
                {
                    href = xattr.Value;
                    break;
                }
            }

            foreach (XElement xel in xelement.Elements())
            {
                if (xel.Name.LocalName == "name")
                {
                    name = xel.Value;
                }
                else if (xel.Name.LocalName == "popularity")
                {
                    if (!string.IsNullOrEmpty(xel.Value))
                    {
                        popularity = double.Parse(xel.Value, new CultureInfo("en-US"));
                    }
                }
                else if (xel.Name.LocalName == "albums")
                {
                    albums = GetAlbums(xel);
                }
            }

            if (albums == null)
            {
                albums = new List<Album>();
            }

            return new ArtistWithAlbums() { Name = name, Popularity = popularity, Href = href, Albums = albums };
        }

        public Artist GetArtist(XElement xelement)
        {
            string name = null;
            double? popularity = null;
            string href = null;

            foreach (XAttribute xattr in xelement.Attributes())
            {
                if (xattr.Name.LocalName == "href")
                {
                    href = xattr.Value;
                    break;
                }
            }

            foreach (XElement xel in xelement.Elements())
            {
                if (xel.Name.LocalName == "name")
                {
                    name = xel.Value;
                }
                else if (xel.Name.LocalName == "popularity")
                {
                    if (!string.IsNullOrEmpty(xel.Value))
                    {
                        popularity = double.Parse(xel.Value, new CultureInfo("en-US"));
                    }
                }
            }

            return new Artist() { Name = name, Popularity = popularity, Href = href };
        }

        public IList<Album> GetAlbums(XElement xelement)
        {
            IList<Album> albums = new List<Album>();

            foreach (XElement xel in xelement.Elements())
            {
                if (xel.Name.LocalName == "album")
                {
                    albums.Add(GetAlbum(xel));
                }
            }

            return albums;
        }

        public AlbumWithTracks GetAlbumWithTracks(XElement xelement)
        {
            string name = null;
            double? popularity = null;
            string href = null;
            int? released = null;
            IList<string> availability = null;
            IList<Id> ids = null;
            Artist artist = null;
            IList<Track> tracks = null;

            foreach (XAttribute xattr in xelement.Attributes())
            {
                if (xattr.Name.LocalName == "href")
                {
                    href = xattr.Value;
                    break;
                }
            }

            foreach (XElement xel in xelement.Elements())
            {
                if (xel.Name.LocalName == "name")
                {
                    name = xel.Value;
                }
                else if (xel.Name.LocalName == "popularity")
                {
                    if (!string.IsNullOrEmpty(xel.Value))
                    {
                        popularity = double.Parse(xel.Value, new CultureInfo("en-US"));
                    }
                }
                else if (xel.Name.LocalName == "released")
                {
                    if (!string.IsNullOrEmpty(xel.Value))
                    {
                        released = int.Parse(xel.Value, new CultureInfo("en-US"));
                    }
                }
                else if (xel.Name.LocalName == "availability")
                {
                    if (!string.IsNullOrEmpty(xel.Value))
                    {
                        availability = GetAlbumAvailability(xel);
                    }
                }
                else if (xel.Name.LocalName == "id")
                {
                    if (ids == null)
                    {
                        ids = new List<Id>();
                    }
                    ids.Add(GetId(xel));
                }
                else if (xel.Name.LocalName == "artist")
                {
                    artist = GetArtist(xel);
                }
                else if (xel.Name.LocalName == "tracks")
                {
                    tracks = GetTracks(xel);
                }
            }

            if (availability == null)
            {
                availability = new List<string>();
            }

            if (tracks == null)
            {
                tracks = new List<Track>();
            }

            return new AlbumWithTracks() { Name = name, Popularity = popularity, Href = href, Released = released, Availability = availability, Ids = ids, Artist = artist, Tracks = tracks };
        }

        public Album GetAlbum(XElement xelement)
        {
            string name = null;
            double? popularity = null;
            string href = null;
            int? released = null;
            IList<string> availability = null;
            IList<Id> ids = null;
            Artist artist = null;

            foreach (XAttribute xattr in xelement.Attributes())
            {
                if (xattr.Name.LocalName == "href")
                {
                    href = xattr.Value;
                    break;
                }
            }

            foreach (XElement xel in xelement.Elements())
            {
                if (xel.Name.LocalName == "name")
                {
                    name = xel.Value;
                }
                else if (xel.Name.LocalName == "popularity")
                {
                    if (!string.IsNullOrEmpty(xel.Value))
                    {
                        popularity = double.Parse(xel.Value, new CultureInfo("en-US"));
                    }
                }
                else if (xel.Name.LocalName == "released")
                {
                    if (!string.IsNullOrEmpty(xel.Value))
                    {
                        released = int.Parse(xel.Value, new CultureInfo("en-US"));
                    }
                }
                else if (xel.Name.LocalName == "availability")
                {
                    if (!string.IsNullOrEmpty(xel.Value))
                    {
                        availability = GetAlbumAvailability(xel);
                    }
                }
                else if (xel.Name.LocalName == "id")
                {
                    if (ids == null)
                    {
                        ids = new List<Id>();
                    }
                    ids.Add(GetId(xel));
                }
                else if (xel.Name.LocalName == "artist")
                {
                    artist = GetArtist(xel);
                }
            }

            if (availability == null)
            {
                availability = new List<string>();
            }

            return new Album() { Name = name, Popularity = popularity, Href = href, Released = released, Availability = availability, Ids = ids, Artist = artist };
        }

        public IList<Track> GetTracks(XElement xelement)
        {
            IList<Track> tracks = new List<Track>();

            foreach (XElement xel in xelement.Elements())
            {
                if (xel.Name.LocalName == "track")
                {
                    tracks.Add(GetTrack(xel));
                }
            }

            return tracks;
        }

        public Track GetTrack(XElement xelement)
        {
            string name = null;
            double? popularity = null;
            string href = null;
            Album album = null;
            IList<Artist> artists = new List<Artist>();
            int? discNumber = null;
            int? trackNumber = null;
            double? length = null;
            bool available = false;
            IList<Id> ids = null;
            

            foreach (XAttribute xattr in xelement.Attributes())
            {
                if (xattr.Name.LocalName == "href")
                {
                    href = xattr.Value;
                    break;
                }
            }

            foreach (XElement xel in xelement.Elements())
            {
                if (xel.Name.LocalName == "name")
                {
                    name = xel.Value;
                }
                else if (xel.Name.LocalName == "popularity")
                {
                    if (!string.IsNullOrEmpty(xel.Value))
                    {
                        popularity = double.Parse(xel.Value, new CultureInfo("en-US"));
                    }
                }
                else if (xel.Name.LocalName == "disc-number")
                {
                    if (!string.IsNullOrEmpty(xel.Value))
                    {
                        discNumber = int.Parse(xel.Value, new CultureInfo("en-US"));
                    }
                }
                else if (xel.Name.LocalName == "track-number")
                {
                    if (!string.IsNullOrEmpty(xel.Value))
                    {
                        trackNumber = int.Parse(xel.Value, new CultureInfo("en-US"));
                    }
                }
                else if (xel.Name.LocalName == "length")
                {
                    if (!string.IsNullOrEmpty(xel.Value))
                    {
                        length = double.Parse(xel.Value, new CultureInfo("en-US"));
                    }
                }
                else if (xel.Name.LocalName == "available")
                {
                    if (!string.IsNullOrEmpty(xel.Value))
                    {
                        available = bool.Parse(xel.Value);
                    }
                }
                else if (xel.Name.LocalName == "id")
                {
                    if (ids == null)
                    {
                        ids = new List<Id>();
                    }
                    ids.Add(GetId(xel));
                }
                else if (xel.Name.LocalName == "artist")
                {
                    artists.Add(GetArtist(xel));
                }
                else if (xel.Name.LocalName == "album")
                {
                    album = GetAlbum(xel);
                }
            }

            return new Track() { Name = name, Href = href, Popularity = popularity, Available = available, DiscNumber = discNumber, TrackNumber = trackNumber, Length = length, Artists = artists, Album = album, Ids = ids };
        }

        public void GetSearchResultMetaData(XElement xelement, out int? totalResults, out int? startIndex, out int? itemsPerPage)
        {
            totalResults = 0;
            startIndex = 0;
            itemsPerPage = 0;

            bool totalResultsFound = false;
            bool startIndexFound = false;
            bool itemsPerPageFound = false;

            foreach (XElement xel in xelement.Elements())
            {
                if (xel.Name.LocalName == "totalResults")
                {
                    if (!string.IsNullOrEmpty(xel.Value))
                    {
                        totalResults = int.Parse(xel.Value, new CultureInfo("en-US"));
                        totalResultsFound = true;
                    }
                }
                else if (xel.Name.LocalName == "startIndex")
                {
                    if (!string.IsNullOrEmpty(xel.Value))
                    {
                        startIndex = int.Parse(xel.Value, new CultureInfo("en-US"));
                        startIndexFound = true;
                    }
                }
                else if (xel.Name.LocalName == "itemsPerPage")
                {
                    if (!string.IsNullOrEmpty(xel.Value))
                    {
                        itemsPerPage = int.Parse(xel.Value, new CultureInfo("en-US"));
                        itemsPerPageFound = true;
                    }
                }

                if (totalResultsFound && startIndexFound && itemsPerPageFound)
                {
                    break;
                }
            }
        }

        private IList<string> GetAlbumAvailability(XElement xelement)
        {
            IList<string> availability = new List<string>();

            foreach (XElement xel in xelement.Elements())
            {
                if (xel.Name.LocalName == "territories")
                {
                    if (!string.IsNullOrEmpty(xel.Value))
                    {
                        if (xel.Value.Contains(' '))
                        {
                            availability = xel.Value.Split(' ').ToList();
                        }
                        else
                        {
                            availability.Add(xel.Value);
                        }
                    }
                }
            }

            return availability;
        }

        private Id GetId(XElement xelement)
        {
            string type = null;
            string value = xelement.Value;

            foreach (XAttribute xattr in xelement.Attributes())
            {
                if (xattr.Name.LocalName == "type")
                {
                    type = xattr.Value;
                    break;
                }
            }

            return new Id() { Type = type, Value = value };
        }
    }
}
